using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Diagnostics;
using System.Xml;
using System.Xml.Schema; 
using XPathParser; 
using System.Xml.Linq; 
using System.Xml.XPath;

namespace CodeGenerationPipelineInterface
{
	[DebuggerDisplay("{ DebugInfo }")]
	public class Root : IXmlElement
	{
        private IXmlDocument document;
        private XNode node;
        private WSDLRootMappedTo wSDLRootMappedTo;
        private ManagedList<Source> typeArguments;
        private ManagedList<Source> arguments;
        private ManagedList<Element> childElements;
        private string name; 
        private const int unbounded = int.MaxValue;

		public Root(IXmlDocument document)
		{
           typeArguments = new ManagedList<Source>("TypeArguments", "Root.TypeArguments", 0, unbounded);
           arguments = new ManagedList<Source>("Arguments", "Root.Arguments", 0, unbounded);
           childElements = new ManagedList<Element>("ChildElements", "Root.ChildElements", 0, unbounded);
            
            this.document = document;
		}

        public IXmlDocument Document 
        { 
            get
            {
                return document;
            }

            set
            {
                document = value;
            }
        }

		[XPathAttribute("/ElementBuild/Elements/Root[@Name]")]
		[UseAttribute(XPathParser.Use.Required)]
		public string Name 
		{
            get
            {
			    return name;
            }

            set
            {
                name = value;
            }
		}

		[XPathAttribute("/ElementBuild/Elements/Root/WSDLRootMappedTo")]
		[MinOccursAttribute(1)]
		[MaxOccursAttribute(1)]
		public WSDLRootMappedTo WSDLRootMappedTo
		{
			get
			{
				return wSDLRootMappedTo;
			}
		}

		[XPathAttribute("/ElementBuild/Elements/Root/TypeArguments/Source")]
		[MinOccursAttribute(0)]
		[MaxOccursAttribute(unbounded)]
 
		public ManagedList<Source> TypeArguments
		{
			get
			{
				return typeArguments;
			}
		}

		[XPathAttribute("/ElementBuild/Elements/Root/TypeArguments/Source")]
		[MinOccursAttribute(0)]
		[MaxOccursAttribute(unbounded)]
 
		public ManagedList<Source> Arguments
		{
			get
			{
				return arguments;
			}
		}

		[XPathAttribute("/ElementBuild/Elements/Root/ChildElements/Element")]
		[MinOccursAttribute(0)]
		[MaxOccursAttribute(unbounded)]
 
		public ManagedList<Element> ChildElements
		{
			get
			{
				return childElements;
			}
		}

		public string ElementName
		{
			get
			{
				return "Root";
			}
		}

		public string DebugInfo
		{
			get
			{
				return "Root";
			}
		}

        public XNode Node
        {
            get
            {
                return node;
            }
        }

        public IEnumerable<string> CDataElements
        {
            get
            {
                var element = (XElement)node;

                return element.Nodes().Where(n => n is XCData).Cast<XCData>().Select(c => c.Value);
            }
        }

        public void Read(XNode node)
        {
            var elementNode = (XElement) node;
            var prefix = elementNode.GetPrefixOfNamespace(elementNode.Name.Namespace);

            // WSDLRootMappedTo

            var elementWSDLRootMappedTo = node.XPathSelectElement(prefix + ":WSDLRootMappedTo", this.Document.NamespaceManager);
            var wSDLRootMappedTo = new WSDLRootMappedTo(this.Document);

            wSDLRootMappedTo.Read(elementWSDLRootMappedTo);

            this.wSDLRootMappedTo  = wSDLRootMappedTo;

            // TypeArguments

            foreach (var elementSource in node.XPathSelectElements(prefix + ":TypeArguments" + "/" + prefix + ":Source", this.Document.NamespaceManager))
            {
                var typeArguments = new Source(this.Document);

                typeArguments.Read(elementSource);

                this.typeArguments.Add(typeArguments);
            }

            // Arguments

            foreach (var elementSource in node.XPathSelectElements(prefix + ":Arguments" + "/" + prefix + ":Source", this.Document.NamespaceManager))
            {
                var arguments = new Source(this.Document);

                arguments.Read(elementSource);

                this.arguments.Add(arguments);
            }

            // ChildElements

            foreach (var elementElement in node.XPathSelectElements(prefix + ":ChildElements" + "/" + prefix + ":Element", this.Document.NamespaceManager))
            {
                var childElements = new Element(this.Document);

                childElements.Read(elementElement);

                this.childElements.Add(childElements);
            }


            // Name

            if (elementNode.Attributes().Any(a => a.Name.LocalName == "Name"))
            {
                name = elementNode.Attribute("Name").Value;
            }
            this.node = node;
        }

        public void Write(XNode parentNode, XNamespace elementNamespace)
        {
            var parentElement = (XElement)parentNode;
            var element = new XElement(elementNamespace + this.ElementName);

            parentElement.Add(element);

            // Name

            element.Add(new XAttribute("Name", this.Name.ToString()));

            var wSDLRootMappedTo = this.WSDLRootMappedTo;

            wSDLRootMappedTo.Write(element, elementNamespace);
            var typeArgumentsElement = new XElement(elementNamespace + "TypeArguments");

            element.Add(typeArgumentsElement);

            foreach (var typeArguments in this.TypeArguments)
            {
                typeArguments.Write(typeArgumentsElement, elementNamespace);
            }

            var argumentsElement = new XElement(elementNamespace + "Arguments");

            element.Add(argumentsElement);

            foreach (var arguments in this.Arguments)
            {
                arguments.Write(argumentsElement, elementNamespace);
            }

            var childElementsElement = new XElement(elementNamespace + "ChildElements");

            element.Add(childElementsElement);

            foreach (var childElements in this.ChildElements)
            {
                childElements.Write(childElementsElement, elementNamespace);
            }

        }

        public void Write(XDocument document, XmlNamespaceManager namespaces)
        {
            var namespaceAttributes = namespaces.Cast<string>().Where(s => !string.IsNullOrEmpty(s) && s != "xmlns" && s != "xml").Select(s => new XAttribute(XNamespace.Xmlns + s, XNamespace.Get(namespaces.LookupNamespace(s))));
            var element = new XElement(this.document.ElementNamespace + this.ElementName, namespaceAttributes.ToArray<object>());

            document.Add(element);

            var wSDLRootMappedTo = this.WSDLRootMappedTo;
            wSDLRootMappedTo.Write(element, this.document.ElementNamespace);

            var typeArgumentsElement = new XElement(this.Document.ElementNamespace + "TypeArguments");

            element.Add(typeArgumentsElement);

            foreach (var typeArguments in this.TypeArguments)
            {
                typeArguments.Write(typeArgumentsElement, this.Document.ElementNamespace);
            }

            var argumentsElement = new XElement(this.Document.ElementNamespace + "Arguments");

            element.Add(argumentsElement);

            foreach (var arguments in this.Arguments)
            {
                arguments.Write(argumentsElement, this.Document.ElementNamespace);
            }

            var childElementsElement = new XElement(this.Document.ElementNamespace + "ChildElements");

            element.Add(childElementsElement);

            foreach (var childElements in this.ChildElements)
            {
                childElements.Write(childElementsElement, this.Document.ElementNamespace);
            }

        }

        public bool Validate(object sender, ValidationEventHandler handler)
        {
            var valid = true;
            WSDLRootMappedTo wSDLRootMappedTo = this.WSDLRootMappedTo;
            wSDLRootMappedTo.Validate(sender, (sender2, e) =>
            {
                handler(sender2, e);
                valid = false;
            });

            foreach (Source typeArguments in this.TypeArguments)
            {
                typeArguments.Validate(sender, (sender2, e) =>
                {
                    handler(sender2, e);
                    valid = false;
                });
			}

            foreach (Source arguments in this.Arguments)
            {
                arguments.Validate(sender, (sender2, e) =>
                {
                    handler(sender2, e);
                    valid = false;
                });
			}

            foreach (Element childElements in this.ChildElements)
            {
                childElements.Validate(sender, (sender2, e) =>
                {
                    handler(sender2, e);
                    valid = false;
                });
			}

            return valid;
        }
    }
}
